﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;


namespace WMH_Project
{

    /*
     * Klasa kontrolująca proces algorytmu.
     * Służy do komunikacji z Widokiem. 
     */
    //TODO Rozszerzyć jej metody i parametry  które są potrzebne dla algorytmu.
    public class AlgorithmController
    {
        struct Command
        {
            public Command(Delegate metho, params object[] arg)
            { method = metho; args = arg; }
            public Delegate method;
            public object[] args;
        }
        private int iter;
        private int itSinceLastBest;
        private int stan = 0;
        private MainForm mainForm;
        private Specimen theBest;
       
        Random random = new Random();
      
        //kolejka komend sterujacych przebiegiem algorytmu
        private CommQueue<Command> acQ = new CommQueue<Command>();
        //funkcja uruchomiona w osobnym watku która obsługuje komendy oraz wykonuje algorytm
        public void Run(MainForm mf)
        {
            mainForm = mf;
            while (true)
            {
                Queue<Command> commands = acQ.Dequeue(stan);
                foreach (Command command in commands)
                {
                    try
                    {
                        command.method.DynamicInvoke(command.args);
                    }
                    catch (Exception ex)
                    {
                        Logger.die("błąd z wywołaniem funkcji:" + ex);
                    }
                }
                string ret = stopCondition.testAndSet(iter);
                if (ret != null)
                {
                    mainForm.Invoke(executed, "finish", stopCondition.StopReason);
                    stan = 2;
                }
                if (stan == 1)
                {
                    iteration();
                    Logger.WriteLine("zakończono iteracje nr: " + iter);
                    ++iter;
                }
            }
        }
        private void iteration()
        {
            Specimen _theBest = theBest;
            for (int i = 0; i < maxPopulation; ++i)
            {
                Specimen spec = specimens[i % startPopulation].Crossover(specimens[(i + startPopulation / 2) % startPopulation]);
                spec.Mutate(mutationParam);
                specimens.Add(spec);
            }
            int all = maxPopulation + startPopulation;
            List<Specimen> newSpecimens = new List<Specimen>();
            
            for (int i = 0; i < startPopulation && specimens.Count > 0; ++i)
            {
                int tmp = random.Next(specimens.Count);
                Specimen winner = specimens[tmp];
                specimens.RemoveAt(tmp);
                for (int j = startPopulation; j < all && specimens.Count > 0; j += startPopulation)
                {
                    tmp = random.Next(specimens.Count);
                    if(winner.GetQuality()<specimens[tmp].GetQuality())
                        winner = specimens[tmp];
                    specimens.RemoveAt(tmp);
                }
                if (_theBest.GetQuality() < winner.GetQuality())
                    _theBest = winner;
                newSpecimens.Add(winner);
            }
            specimens = newSpecimens;
            itSinceLastBest++;
            IterationStat itStat = new IterationStat(iter, itSinceLastBest ,specimens); 
            
            mainForm.Invoke(nextIteration, itStat);
            setNewBest(_theBest);
        }
        private void setNewBest(Specimen _theBest)
        {
            if (theBest == _theBest)
                return;
            itSinceLastBest = 0;
            theBest = _theBest;
            stopCondition.setNewBest(theBest, iter);
            mainForm.Invoke(newBest, theBest);

        }
        // Uruchomienie algorytmu. Odpala on się w osobnym wątku.
        // Innymi słowy przepływ sterowania musi powrócić z tej metody jak najszybciej.
        private delegate void StartD(int b, int c, StopCondition sc, MutationParam mp);
        public void Start(int b, int c, StopCondition sc, MutationParam mp) { acQ.Enqueue(new Command(new StartD(StartI), b, c, sc, mp)); }
        private void StartI(int b, int c, StopCondition sc, MutationParam mp)
        {
            if (newBest == null)
                Logger.die("nie ustawino delegata newBest");
            if (executed == null)
                Logger.die("nie ustawino delegata finish");
            if (specimenFactory == null)
                Logger.die("nie ustawino specimenFactory");
            stopCondition = sc;
            mutationParam = mp;
            startPopulation = b;
            maxPopulation = c;
            iter = 0;
            itSinceLastBest = 0;
            stan = 1;
            specimens = new List<Specimen>();
            theBest = specimenFactory.GetSpecimen();
            specimens.Add(theBest);
            for (int i = 1; startPopulation > i; ++i){
                Specimen tmp = specimenFactory.GetSpecimen();
                if (theBest.GetQuality() < tmp.GetQuality())
                    theBest = tmp;
                specimens.Add(tmp);
            }
            setNewBest(theBest);

        

            //specimens.ForEach(delegate(Specimen s) { s.GetQuality(); });
            mainForm.Invoke(executed, "start",null);
            Logger.WriteLine("AlgorithmController.Start(): Start");
        }
        //metoda pozwalająca na zatrzymanie algorytmu w dowolnym momencie.
        private delegate void StopD();
        public void Stop() { acQ.Enqueue(new Command(new StopD(StopI))); }
        private void StopI()
        {
            stan = 0;
            if (iter != 0)
            {
                mainForm.Invoke(executed, "stop", null);
            }
            Logger.WriteLine("AlgorithmController.Stop()");
        }
        //metoda pozwalająca na zatrzymanie algorytmu w dowolnym momencie.
        private delegate void ContinueD();
        public void Continue() { acQ.Enqueue(new Command(new ContinueD(ContinueI))); }
        private void ContinueI()
        {
            stan = 1;
            mainForm.Invoke(executed, "start",null);
            Logger.WriteLine("AlgorithmController.Continue()");
        }

        //zdażenie polegające na tym ze został znaleziony nowy najlepszy osobnik
        public delegate void NewBest(Specimen best);
        public NewBest newBest = null;
        //zdażenie polegające na tym ze algorytm zakończył działanie
        public delegate void Executed(string typ, STOP_REASON stopReason);
        public Executed executed = null;
        //zadarzenie polegające na tym że zaszła kolejna iteracja
        public delegate void NextIteration(IterationStat itStat);
        public NextIteration nextIteration = null;



        
        private StopCondition stopCondition;
        private MutationParam mutationParam;
        //Populacja początkowa
        private int startPopulation;
        //Maksymalna wielkość populacji
        private int maxPopulation;
        //Fabryka losowych specimen-tów służy głównie do generowania nowej populacji
        private SpecimenFactory specimenFactory = null;
        private List<Specimen> specimens = null;

        public SpecimenFactory SpecimenFactory
        {
            get { return specimenFactory; }
            set { specimenFactory = value; }
        }
    }
}
